#include "ConditionVar.hpp"
#include "../base/BaseLog.hpp"
#include <cerrno>
#include <cstring>
#include <stdexcept>
#include <fcntl.h>
#include <unistd.h>

using namespace DBServer::coroutine;

ConditionVar::ConditionVar()
:   count(0) {
    int rt = pipe(chan);
    if (rt < 0)
    {
        error("pipe() fail[%d]: %s\n", errno, strerror(errno));
        throw;
    }

    auto Fcntl = [](int fd)
    {
        int attr = fcntl(fd, F_GETFD, 0);
        attr |= O_NONBLOCK;
        fcntl(fd, F_SETFD, attr);
    };

    Fcntl(chan[0]);
    Fcntl(chan[1]);
}

ConditionVar::~ConditionVar() {
    if (count != 0) 
    {
        error("count != 0(is %d)\n", count.load());
    }

    close(chan[0]);
    close(chan[1]);
}

static char notify = 'T';

void ConditionVar::notify_all() {
    for (int i = 0; i < count; ++ i)
    {
        notify_one();
    }
}

void ConditionVar::notify_one() {
    if (count > 0) {
        write(chan[1], &notify, 1);
    }
}

void ConditionVar::wait() {
    int rt;
    char buf[1];
    ++count;
    while (true)
    {
        rt = read(chan[0], buf, 1);
        if (rt == -1 &&  errno == EINTR) {
            continue;
        } else if (rt == -1 && errno == EAGAIN) {
            continue;
        } else {
            break;
        }
    }
    --count;
}

void ConditionVar::wait(std::function<bool()> condition) {
    int rt;
    char buf[1];
    ++count;
    // 在条件不为true之前没有必要接收唤醒信号，
    // 因为这样会导致虚假唤醒，还会浪费唤醒信号
    while (!condition())
        ;
    while (true)
    {
        rt = read(chan[0], buf, 1);
        if (rt == -1 &&  errno == EINTR) {
            continue;
        } else if (rt == -1 && errno == EAGAIN) {
            continue;
        } else {
            break;
        }
    }
    --count;
}